package com.example.stock.entity.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.stock.comm.Cons;
import com.example.stock.comm.FreqEnum;
import com.example.stock.comm.StrategyEnum;
import com.example.stock.entity.dao.ProBarStockDao;
import com.example.stock.entity.eo.ProBarStockEo;
import com.example.stock.entity.eo.StStockTradeEo;
import com.example.stock.entity.eo.StockTradeEo;
import com.example.stock.entity.mapper.ProBarStockMapper;
import com.example.stock.entity.service.IProBarStockService;
import com.example.stock.service.impl.ServiceCalcImpl;
import com.example.stock.vo.DataVo;
import com.example.stock.vo.EmaTradeVo;
import com.example.stock.vo.common.TradeVo;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ProBarStockServiceImpl extends ServiceImpl<ProBarStockMapper, ProBarStockEo> implements IProBarStockService {

    private static final Double CHARGE_RATE = 0.0001;
    private static final Double SALE_CHARGE_RATE = 0.0012;

    @Resource
    private ProBarStockMapper proBarStockMapper;
    @Resource
    private ProBarStockDao proBarStockDao;
    @Resource
    private ServiceCalcImpl calcServiceImpl;

    public List<ProBarStockEo> getAll(String tsCode){
        return proBarStockMapper.selectList(Wrappers.<ProBarStockEo>lambdaQuery()
                .eq(ProBarStockEo::getTsCode, tsCode)
                .orderByAsc(ProBarStockEo::getTradeTime));

    }

    //获取30分钟收盘价
    public List<DataVo> getRowData30(String tsCode, LocalDateTime startTime){
        final LocalDateTime endTime = startTime == null ? LocalDateTime.MIN : startTime;
        List<ProBarStockEo> proBarStockEos = getAll(tsCode);
        return proBarStockEos.stream()
                .filter(t -> FreqEnum.MIN30.listTime().contains(t.getTradeTime().format(Cons.formatHHmm)))
                .filter(t -> t.getTradeTime().compareTo(endTime) >= 0)
                .map(t -> {
                    DataVo dataVo = new DataVo();
                    dataVo.setTradeTime(t.getTradeTime());
                    dataVo.setPrice(t.getClose());
                    return dataVo;
                }).collect(Collectors.toList());
    }


    public List<DataVo> getRowData60(String tsCode, LocalDateTime startTime) {
        final LocalDateTime endTime = startTime == null ? LocalDateTime.MIN : startTime;
        List<ProBarStockEo> proBarStockEos = getAll(tsCode);
        return proBarStockEos.stream()
                .filter(t -> Cons.time60Min.contains(t.getTradeTime().format(Cons.formatHHmm)))
                .filter(t -> t.getTradeTime().compareTo(endTime) >= 0)
                .map(t -> {
                    DataVo dataVo = new DataVo();
                    dataVo.setTradeTime(t.getTradeTime());
                    dataVo.setPrice(t.getClose());
                    return dataVo;
                }).collect(Collectors.toList());
    }

    public Map<String, List<DataVo>> getRowData30Batch(LocalDateTime startTime, LocalDateTime endTime, List<String> tsCodes,int skip) {
        Map<String, List<DataVo>> stringListMap = proBarStockDao.mapDataVo30(tsCodes, startTime, endTime);
        return stringListMap;
    }

    public Map<String, List<TradeVo>> calcEmaUpTrade(Map<String, List<DataVo>> stockRawData) {
        Map<String, List<TradeVo>>  ret = new HashMap<>();
        stockRawData.forEach((k,v)->{
            List<TradeVo> tradeVos = calcServiceImpl.calcEmaUpTrade(v, 70, 100);
            ret.put(k, tradeVos);
        });
        return ret;
    }

    public List<StockTradeEo> calcTrade(List<TradeVo> tradeVos, String tsCode) {
        List<StockTradeEo> stockTradeEos = new ArrayList<>();
        StStockTradeEo tradeEo = new StStockTradeEo()
                .setMoney(1000000.0)
                .setHoldStatus((short) 0);

        for (TradeVo tradeVo : tradeVos) {
            Double price = tradeVo.getPrice();
            if (tradeVo.isCanBuy()) {
                if (emptyHold(tradeEo)) {
                    //可用金额(扣除手续费)
                    Double totalM = tradeEo.getMoney() - (tradeEo.getMoney() * CHARGE_RATE);
                    tradeEo.setHoldStatus((short) 1);
                    tradeEo.setHoldTime(tradeVo.getTradeTime());
                    tradeEo.setCharge(tradeEo.getMoney()*CHARGE_RATE);
                    tradeEo.setHoldVol(totalM / price);
                    tradeEo.setMoney(totalM);
                    tradeEo.setPrice(price);
                    tradeEo.setProfit(0.0);

                    tradeEo.setTsCode(tsCode);
                    tradeEo.setStrategy(StrategyEnum.EMA_UP.name());

                    if (tradeVo instanceof EmaTradeVo) {
                        tradeEo.setCalcResult(((EmaTradeVo) tradeVo).getCalc1());
                    }
                    stockTradeEos.add(BeanUtil.copyProperties(tradeEo,StockTradeEo.class));
                }

            } else {
                if (!emptyHold(tradeEo)) {
                    //当天进行过交易
                    if (tradeVo.getTradeTime().getDayOfYear() == tradeEo.getHoldTime().getDayOfYear()) {
                        continue;
                    }
                    //金额 = 持仓*当前收盘价*(1-手续费)
                    Double money = tradeEo.getHoldVol() * price * (1.0 - SALE_CHARGE_RATE);
                    tradeEo.setProfit(money - tradeEo.getMoney());
                    tradeEo.setMoney(money);
                    tradeEo.setCharge(tradeEo.getHoldVol()*price*SALE_CHARGE_RATE);
                    tradeEo.setHoldVol(0.0);
                    tradeEo.setHoldStatus((short) 0);
                    tradeEo.setHoldTime(tradeVo.getTradeTime());
                    tradeEo.setPrice(tradeVo.getPrice());

                    tradeEo.setTsCode(tsCode);
                    tradeEo.setStrategy(StrategyEnum.EMA_UP.name());

                    if (tradeVo instanceof EmaTradeVo) {
                        tradeEo.setCalcResult(((EmaTradeVo) tradeVo).getCalc1());
                    }
                    stockTradeEos.add(BeanUtil.copyProperties(tradeEo,StockTradeEo.class));
                }
            }
        }
        return stockTradeEos;

    }

    private boolean emptyHold(StStockTradeEo hold) {
        return hold.getHoldStatus() == null || hold.getHoldStatus() == 0;
    }

    public Map<String, List<StockTradeEo>> doTrade(Map<String, List<TradeVo>> stockTradeVo) {
        Map<String, List<StockTradeEo>>  ret = new HashMap<>();
        stockTradeVo.forEach((k,v)->{
            List<StockTradeEo> stockTradeEos = calcTrade(v, k);
            ret.put(k, stockTradeEos);
        });
        return ret;
    }

    public List<StockTradeEo> combineTrade(Map<String, List<StockTradeEo>> collect) {
        List<StockTradeEo> stockTradeEos = new ArrayList<>();
        Collection<List<StockTradeEo>> values1 = collect.values();

        Map<String,StockTradeEo> firstDay = new HashMap<>();
        //排序及金额处理
        collect.forEach((k,v)-> handlerList(v));

//        LocalDateTime holdTime = tempList.get(0).getHoldTime();
        List<List<LocalDateTime>> collect2 = values1.stream().map(v -> v.stream().map(StockTradeEo::getHoldTime).collect(Collectors.toList())).collect(Collectors.toList());
        List<LocalDateTime> tempTimes = new ArrayList<>();
        for (List<LocalDateTime> localDateTimes : collect2) {
            tempTimes.addAll(localDateTimes);
        }
        tempTimes.sort(Comparator.comparing(t -> t));

        collect.forEach((k,v)->{
            StockTradeEo stockTradeEo = v.get(0);
            StockTradeEo first = BeanUtil.copyProperties(stockTradeEo, StockTradeEo.class);
            first.setHoldStatus((short) 0);
            first.setHoldTime(tempTimes.get(0));
            firstDay.put(k, first);
        });


//        List<LocalDateTime> allTradeDay = tempList.stream().map(StockTradeEo::getHoldTime).distinct().collect(Collectors.toList());
        List<LocalDateTime> allTradeDay = tempTimes;
        validAllTradeDay(allTradeDay);

//        for (LocalDateTime localDateTime : allTradeDay) {
//            List<StockTradeEo> collect1 = tempList.stream().filter(t -> t.getHoldTime().equals(localDateTime)).collect(Collectors.toList());
//            for (StockTradeEo stockTradeEo : collect1) {
//                firstDay.put(stockTradeEo.listTsCode(), stockTradeEo);
//            }
//            Collection<StockTradeEo> values = firstDay.values();
//            stockTradeEos.add(new StockTradeEo()
//                    .setHoldTime(localDateTime)
//                    .setPrice(values.stream().mapToDouble(StockTradeEo::getPrice).sum()/(values.size()))
//                    .setMoney(values.stream().mapToDouble(StockTradeEo::getMoney).sum()/(values.size()))
//            );
//        }
        return null;

    }

    private void validAllTradeDay(List<LocalDateTime> allTradeDay) {
        LocalDateTime pre = LocalDateTime.MIN;
        for (LocalDateTime localDateTime : allTradeDay) {
            if (localDateTime.isBefore(pre)) {
                throw new RuntimeException("时间有误!");
            }
            pre = localDateTime;
        }
    }


    //先排序
    private void handlerList(List<StockTradeEo> v) {
//    v.stream().sorted(Comparator.comparing(StockTradeEo::getHoldTime));
        double base = v.get(0).getPrice();
        v.forEach(eo-> eo.setPrice(eo.getPrice()/ base * 1000000.0));
    }
}
